/*
 * Copyright (c) Huawei Device Co., Ltd. 2023. All rights reserved.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#include "session.h"
#include "channel.h"
#include "isecure_element_channel.h"
#include "isecure_element_session.h"
#include "loghelper.h"
#include "reader.h"
#include "se_service.h"

namespace OHOS {
namespace SE {
namespace OMAPI {
Session::Session(std::weak_ptr<SEService> service, sptr<ISecureElementSession> session, std::weak_ptr<Reader> reader)
    : sessionIface_(session), boundService_(service), boundReader_(reader)
{
}

Session::~Session() {}
int Session::InnerCheckSessionStatus(void)
{
    // NOTE: don't lock for mutex_, already locked outter.
    if (boundService_.expired() || !sessionIface_) {
        ErrorLog("InnerCheckSessionStatus boundService_ or readerIface_ is null!");
        return ErrorCode::SE_ILLEGAL_STATE_ERROR;
    }
    return ErrorCode::SE_SUCCESS;
}

int Session::GetReader(std::weak_ptr<Reader> &reader)
{
    std::lock_guard<std::mutex> lock(mutex_);
    reader = boundReader_;
    return ErrorCode::SE_SUCCESS;
}

int Session::GetATR(std::string &atr)
{
    std::lock_guard<std::mutex> lock(mutex_);
    atr = std::string();
    int ret = InnerCheckSessionStatus();
    if (ret != ErrorCode::SE_SUCCESS) {
        ErrorLog("GetATR failed, res = %{public}d", ret);
        return ret;
    }

    return sessionIface_->GetATR(atr);
}

int Session::Close(void)
{
    std::lock_guard<std::mutex> lock(mutex_);
    int ret = InnerCheckSessionStatus();
    if (ret != ErrorCode::SE_SUCCESS) {
        ErrorLog("Close failed, res = %{public}d", ret);
        return ret;
    }

    return sessionIface_->Close();
}

int Session::IsClosed(bool &isClosed)
{
    std::lock_guard<std::mutex> lock(mutex_);
    isClosed = true;
    int ret = InnerCheckSessionStatus();
    if (ret != ErrorCode::SE_SUCCESS) {
        ErrorLog("IsClosed failed, res = %{public}d", ret);
        return ret;
    }

    return sessionIface_->IsClosed(isClosed);
}

int Session::CloseChannels(void)
{
    std::lock_guard<std::mutex> lock(mutex_);
    int ret = InnerCheckSessionStatus();
    if (ret != ErrorCode::SE_SUCCESS) {
        ErrorLog("CloseChannels failed, res = %{public}d", ret);
        return ret;
    }

    return sessionIface_->CloseChannels();
}

int Session::OpenBasicChannel(const std::string &aid, int p2, std::shared_ptr<Channel>& channel, std::string &apduResp)
{
    std::lock_guard<std::mutex> lock(mutex_);
    channel = std::shared_ptr<Channel>();
    int ret = InnerCheckSessionStatus();
    if (ret != ErrorCode::SE_SUCCESS) {
        ErrorLog("OpenBasicChannel failed, check status ret = %{public}d", ret);
        return ret;
    }
    sptr<ISecureElementChannel> channelIface;
    ret = sessionIface_->OpenBasicChannel(aid, p2, channelIface, apduResp);
    if (ret != ErrorCode::SE_SUCCESS) {
        ErrorLog("OpenBasicChannel failed, OpenBasicChannel ret = %{public}d", ret);
        return ret;
    }
    if (!channelIface) {
        ErrorLog("OpenBasicChannel channelIface is null");
        return ErrorCode::SE_CHANNEL_NOT_AVAILABLE_ERROR;
    }
    channel = std::make_shared<Channel>(boundService_, shared_from_this(), channelIface);
    return ret;
}

int Session::OpenBasicChannel(const std::string &aid, std::shared_ptr<Channel>& channel, std::string &apduResp)
{
    // inner call, don't guard(mutex_) here again.
    return OpenBasicChannel(aid, 0x00, channel, apduResp);
}

int Session::OpenLogicalChannel(const std::string &aid, int p2, std::shared_ptr<Channel>& channel,
    std::string &apduResp)
{
    std::lock_guard<std::mutex> lock(mutex_);
    channel = std::shared_ptr<Channel>();
    int ret = InnerCheckSessionStatus();
    if (ret != ErrorCode::SE_SUCCESS) {
        ErrorLog("OpenLogicalChannel failed, check status ret = %{public}d", ret);
        return ret;
    }
    sptr<ISecureElementChannel> channelIface;
    ret = sessionIface_->OpenLogicalChannel(aid, p2, channelIface, apduResp);
    if (ret != ErrorCode::SE_SUCCESS) {
        ErrorLog("OpenLogicalChannel failed, OpenBasicChannel ret = %{public}d", ret);
        return ret;
    }
    if (!channelIface) {
        ErrorLog("OpenLogicalChannel channelIface is null");
        return ErrorCode::SE_CHANNEL_NOT_AVAILABLE_ERROR;
    }
    channel = std::make_shared<Channel>(boundService_, shared_from_this(), channelIface);
    return ret;
}

int Session::OpenLogicalChannel(const std::string& aid, std::shared_ptr<Channel>& channel, std::string &apduResp)
{
    // inner call, don't guard(mutex_) here again.
    return OpenLogicalChannel(aid, 0x00, channel, apduResp);
}
}  // namespace OMAPI
}  // namespace SE
}  // namespace OHOS